﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.Windows.Forms;
using log4net;
using ResxCrunch.Business;
using ResxCrunch.Business.Managers;
using ResxCrunch.Plugins;
using ResxCrunch.Properties;
using ResxCrunch.Utils;
using System.ComponentModel.Composition.Hosting;

namespace ResxCrunch
{
    /// <summary>
    /// Main form class
    /// </summary>
    public partial class FrmMain : Form
    {
        #region Constants
        /// <summary>
        /// New Datatables are created with this name
        /// </summary>
        private const string InititalTableName = "Data";

        /// <summary>
        /// Default status label text displayed
        /// </summary>
        private readonly string DefaultStatusText = string.Empty;
        //private const string DefaultStatusText = "Working...";

        /// <summary>
        /// Logger declaration
        /// </summary>
        private static readonly ILog log = LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
        #endregion Constants

        #region Attributes
        /// <summary>
        /// Table used to contain data that needs top be accessed from more threads
        /// </summary>
        private DataTable tempTable = null;

        /// <summary>
        /// Stores the last loaded data's info
        /// </summary>
        private object lastLoadedInfo = null;

        /// <summary>
        /// Stores the last search string
        /// </summary>
        private List<string> lastFindStrings = new List<string>();

        /// <summary>
        /// Last used find's search up setting
        /// </summary>
        private bool lastFindUpSetting = false;

        /// <summary>
        /// Last used find's case sensitive setting
        /// </summary>
        private bool lastFindCaseSensitive = false;

        /// <summary>
        /// If it is true after save the application closes
        /// </summary>
        private bool closeAfterSaveFlag = false;

        /// <summary>
        /// This is the data saved after an operation to be restored with undo
        /// </summary>
        private DataTable undoData = null;

        /// <summary>
        /// Contains the filtered data data
        /// </summary>
        private DataTable filteredData = null;

        /// <summary>
        /// The loaded plugins of the applications
        /// </summary>
        Dictionary<string, IResxCrunchPlugin> loadedPlugins = new Dictionary<string, IResxCrunchPlugin>();
        #endregion Attributes

        #region Properties
        /// <summary>
        /// Gets or sets the registration information.
        /// </summary>
        /// <value>The register info.</value>
        public RegistrationData RegisterInfo { get; set; }

        /// <summary>
        /// Sets a value indicating whether this <see cref="FrmMain"/> is busy.
        /// </summary>
        /// <value><c>true</c> if busy; otherwise, <c>false</c>.</value>
        public bool Busy
        {
            set
            {
                if (value)
                {
                    menuMain.Enabled = false;
                    toolStripMain.Enabled = false;
                    grdResx.Enabled = false;

                    lblStatus.Text = string.Empty;
                    tsProgressbar.Visible = true;

                    this.Refresh();
                }
                else
                {
                    menuMain.Enabled = true;
                    toolStripMain.Enabled = true;
                    grdResx.Enabled = true;

                    tsProgressbar.Visible = false;
                    tsProgressbar.Value = 0;

                    // Values
                    this.SetStatus(null, null, false);
                }
            }
        }

        /// <summary>
        /// Gets or sets a value indicating whether the loaded data needs save.
        /// </summary>
        /// <value><c>true</c> if save needed; otherwise, <c>false</c>.</value>
        public bool NeedsSave
        {
            get;
            set;
        }

        /// <summary>
        /// Gets or sets the copy def value options.
        /// </summary>
        /// <value>The copy def value options.</value>
        public CopyDefultValuesOptions CopyDefValueOptions { get; set; }
        #endregion Properties

        #region Initialization
        /// <summary>
        /// Initializes a new instance of the FrmMain class
        /// </summary>
        public FrmMain()
        {
            InitializeComponent();
        }

        /// <summary>
        /// Handles the Load event of the FrmMain control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void FrmMain_Load(object sender, EventArgs e)
        {
            this.LoadRegistrationInfo();
            this.LoadForm();
            this.Enablings();

            this.LoadPlugins();
        }

        /// <summary>
        /// Loads the plugins from the plugin folder.
        /// </summary>
        private void LoadPlugins()
        {
            this.StartJob(false, "Loading plugins", Resources.Gear01, false);

            List<IResxCrunchPlugin> pluginList = PluginManager.Instance.LoadAllPlugins(Settings.Default.PluginsPath);

            if (pluginList.Count > 0)
            {
                pluginPlaceholderToolStripMenuItem.Visible = false;

                foreach (IResxCrunchPlugin plugin in pluginList)
                {
                    string pluginKey = string.Format("tsi_{0}", plugin.GetType().FullName);
                    ToolStripMenuItem tsi = new ToolStripMenuItem(plugin.Text, plugin.MenuImage, PluginMenuItems_Click, pluginKey);
                    pluginsToolStripMenuItem.DropDownItems.Add(tsi);
                    loadedPlugins.Add(pluginKey, plugin);
                }
            }

            //DirectoryCatalog dirCat = new DirectoryCatalog(".");
            //CompositionContainer container = new CompositionContainer(dirCat);
            //CompositionBatch compBatch = new CompositionBatch();
            //compBatch.AddPart(this);
            //container.Compose(compBatch);
            //plug.Add();

            this.EndJob(false, string.Format("{0} plugins loaded", pluginList.Count), false);
        }

        private void PluginMenuItems_Click(object sender, EventArgs e)
        {
            IResxCrunchPlugin plugin = loadedPlugins[((ToolStripMenuItem)sender).Name];
            plugin.InitializePlugin();

            this.StartJob(true, "Executing plugin...", Resources.Gear01, false);

            DataTable data = (DataTable)grdResx.DataSource;

            // Create backup
            DataTable backup = data.Copy();

            // Remove the datasource
            grdResx.DataSource = null;

            int counter;
            string message = plugin.ProcessData(ref data, out counter);

            if (message.Length == 0)
            {
                grdResx.DataSource = data;

                // Save undo information
                undoData = backup;
            }
            else
            {
                MessageBox.Show(
                                this,
                                string.Format("Plugin '{0}' encountered an error during execution with the following message: {1}", plugin.Text, message),
                                Settings.Default.AppName,
                                MessageBoxButtons.OK,
                                MessageBoxIcon.Warning);

                grdResx.DataSource = backup;
            }

            this.EndJob(
                        true,
                        message.Length == 0 ? string.Format(
                                                            "Plugin executed successfully. {0} rows removed.",
                                                            counter)
                                            : "Plugin did not complete operation.",
                        message.Length == 0);
        }
        #endregion Initialization

        #region Important methods
        /// <summary>
        /// Enables disables items on the form according to the situation
        /// </summary>
        private void Enablings()
        {
            loadAdditionalMenuItem.Enabled = true;
            loadPatchToolStripMenuItem.Enabled = true;
            saveMenuItem.Enabled = true;
            saveAsMenuItem.Enabled = true;

            findToolStripMenuItem.Enabled = true;
            findPreviousToolStripMenuItem.Enabled = true;
            findNextToolStripMenuItem.Enabled = true;

            removeFileMenuItem.Enabled = true;
            removeEmptyRowsMenuItem.Enabled = true;
            addLanguageMenuItem.Enabled = true;
            removeLanguageMenuItem.Enabled = true;
            removeKeysMenuItem.Enabled = true;
            copyDefaultValueMenuItem.Enabled = true;
            pluginsToolStripMenuItem.Enabled = true;

            clearAllDataMenuItem.Enabled = true;
            undoToolStripMenuItem.Enabled = true;

            if (grdResx.DataSource == null)
            {
                #region Datasource null
                loadAdditionalMenuItem.Enabled = false;
                loadPatchToolStripMenuItem.Enabled = false;
                saveMenuItem.Enabled = false;
                saveAsMenuItem.Enabled = false;

                findToolStripMenuItem.Enabled = false;

                removeFileMenuItem.Enabled = false;
                removeEmptyRowsMenuItem.Enabled = false;
                addLanguageMenuItem.Enabled = false;
                removeLanguageMenuItem.Enabled = false;
                removeKeysMenuItem.Enabled = false;
                copyDefaultValueMenuItem.Enabled = false;
                findNextToolStripMenuItem.Enabled = false;
                findPreviousToolStripMenuItem.Enabled = false;
                pluginsToolStripMenuItem.Enabled = false;

                clearAllDataMenuItem.Enabled = false;
                #endregion Datasource null
            }
            else
            {
                #region Datasource NOT null
                if (grdResx.RowCount <= 1)
                {
                    #region 0 rows
                    findToolStripMenuItem.Enabled = false;
                    findPreviousToolStripMenuItem.Enabled = false;
                    findNextToolStripMenuItem.Enabled = false;

                    removeFileMenuItem.Enabled = false;
                    removeEmptyRowsMenuItem.Enabled = false;
                    removeKeysMenuItem.Enabled = false;
                    copyDefaultValueMenuItem.Enabled = false;
                    #endregion 0 rows
                }

                if (grdResx.ColumnCount == 0)
                {
                    #region 0 cols
                    findToolStripMenuItem.Enabled = false;
                    findPreviousToolStripMenuItem.Enabled = false;
                    findNextToolStripMenuItem.Enabled = false;

                    copyDefaultValueMenuItem.Enabled = false;

                    removeLanguageMenuItem.Enabled = false;
                    #endregion 0 cols
                }

                if (!grdResx.Columns.Contains(Enums.DataColumn.Directory) || !grdResx.Columns.Contains(Enums.DataColumn.File))
                {
                    removeFileMenuItem.Enabled = false;
                }

                if (!grdResx.Columns.Contains(Enums.DataColumn.Key))
                {
                    removeKeysMenuItem.Enabled = false;
                }

                if (!grdResx.Columns.Contains(Enums.DataColumn.DefaultValue))
                {
                    copyDefaultValueMenuItem.Enabled = false;
                }
                #endregion Datasource NOT null
            }

            #region Undo
            if (this.undoData == null)
            {
                undoToolStripMenuItem.Enabled = false;
            }

            if (lastFindStrings.Count == 0)
            {
                findPreviousToolStripMenuItem.Enabled = false;
                findNextToolStripMenuItem.Enabled = false;
            }
            #endregion Undo

            #region Update values
            if (lastLoadedInfo != null)
            {
                string typeName = lastLoadedInfo.GetType().Name;

                switch (typeName.ToUpper())
                {
                    case "RESXGROUPDATA":
                        Utils.FileData.ResxGroupData resxGroupData = (Utils.FileData.ResxGroupData)lastLoadedInfo;
                        this.Text = string.Format("{0} [{1}] [{2}{3}]", Settings.Default.AppName, typeName, this.NeedsSave ? "* " : string.Empty, resxGroupData.Directory);
                        break;
                    case "RESXDATA":
                        Utils.FileData.ResxData resxData = (Utils.FileData.ResxData)lastLoadedInfo;
                        this.Text = string.Format("{0} [{1}] [{2}{3}]", Settings.Default.AppName, typeName, this.NeedsSave ? "* " : string.Empty, resxData.Path);
                        break;
                    case "RESOURCEGROUPDATA":
                        Utils.FileData.ResourceGroupData resourceGroupData = (Utils.FileData.ResourceGroupData)lastLoadedInfo;
                        this.Text = string.Format("{0} [{1}] [{2}{3}]", Settings.Default.AppName, typeName, this.NeedsSave ? "* " : string.Empty, resourceGroupData.Directory);
                        break;
                    case "RESOURCEDATA":
                        Utils.FileData.ResourceData resourceData = (Utils.FileData.ResourceData)lastLoadedInfo;
                        this.Text = string.Format("{0} [{1}] [{2}{3}]", Settings.Default.AppName, typeName, this.NeedsSave ? "* " : string.Empty, resourceData.Path);
                        break;
                    case "CSVDATA":
                        Utils.FileData.CsvData csvData = (Utils.FileData.CsvData)lastLoadedInfo;
                        this.Text = string.Format("{0} [{1}] [{2}{3}]", Settings.Default.AppName, typeName, this.NeedsSave ? "* " : string.Empty, csvData.Path);
                        break;
                    case "XMLDATA":
                        Utils.FileData.XmlData xmlData = (Utils.FileData.XmlData)lastLoadedInfo;
                        this.Text = string.Format("{0} [{1}] [{2}{3}]", Settings.Default.AppName, typeName, this.NeedsSave ? "* " : string.Empty, xmlData.Path);
                        break;
                    case "ANDROIDGROUPDATA":
                        Utils.FileData.AndroidGroupData androidGroupData = (Utils.FileData.AndroidGroupData)lastLoadedInfo;
                        this.Text = string.Format("{0} [{1}] [{2}{3}]", Settings.Default.AppName, typeName, this.NeedsSave ? "* " : string.Empty, androidGroupData.Directory);
                        break;
                    case "BLACKBERRYGROUPDATA":
                        Utils.FileData.BlackberryGroupData blackberryGroupData = (Utils.FileData.BlackberryGroupData)lastLoadedInfo;
                        this.Text = string.Format("{0} [{1}] [{2}{3}]", Settings.Default.AppName, typeName, this.NeedsSave ? "* " : string.Empty, blackberryGroupData.Directory);
                        break;
                }
            }
            else
            {
                this.Text = Settings.Default.AppName;
                saveMenuItem.Enabled = false;
            }
            #endregion Update values

            lblNrRows.Text = string.Format("{0} items {1} filtered", grdResx.RowCount, filteredData == null ? 0 : filteredData.Rows.Count);

            #region Registration Info
            if (RegisterInfo == null)
            {
                MessageBox.Show(this, "Could not load register info", Settings.Default.AppName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                this.Close();
            }

            if (RegisterInfo.Email.Equals(Constants.TrialEmailAddress, StringComparison.InvariantCultureIgnoreCase))
            {
                // Disable functionality
                copyDefaultValueMenuItem.Enabled = false;

                // Hide elements
                activateLicenseToolStripMenuItem.Visible = true;
                purchaseLicenseToolStripMenuItem.Visible = true;
                changeSerialToolStripMenuItem.Visible = false;
                purchaseAnotherLicenseToolStripMenuItem.Visible = false;
            }
            else
            {
                activateLicenseToolStripMenuItem.Visible = false;
                purchaseLicenseToolStripMenuItem.Visible = false;
                changeSerialToolStripMenuItem.Visible = true;
                purchaseAnotherLicenseToolStripMenuItem.Visible = true;
            }
            #endregion Registration Info

            #region Copy menu behavior to toolstrip
            tsbLoadAdditional.Enabled = loadAdditionalMenuItem.Enabled;
            tsbLoadPatch.Enabled = loadPatchToolStripMenuItem.Enabled;
            tsbSave.Enabled = saveMenuItem.Enabled;
            tsbSaveAs.Enabled = saveAsMenuItem.Enabled;
            tsbFind.Enabled = findToolStripMenuItem.Enabled;
            tsbFindPrev.Enabled = findPreviousToolStripMenuItem.Enabled;
            tsbFindNext.Enabled = findNextToolStripMenuItem.Enabled;
            tsbFiltersEnabled.Checked = enableFiltersToolStripMenuItem.Checked;

            tsbRemoveFiles.Enabled = removeFileMenuItem.Enabled;
            tsbRemoveEmptyRows.Enabled = removeEmptyRowsMenuItem.Enabled;
            tsbAddLanguage.Enabled = addLanguageMenuItem.Enabled;
            tsbRemoveLanguageColumn.Enabled = removeLanguageMenuItem.Enabled;
            tsbRemoveKeys.Enabled = removeKeysMenuItem.Enabled;

            tsbClearAllData.Enabled = clearAllDataMenuItem.Enabled;
            tsbUndo.Enabled = undoToolStripMenuItem.Enabled;
            #endregion Copy menu behavior to toolstrip
        }

        /// <summary>
        /// Sets the statusbar status
        /// </summary>
        /// <param name="text"></param>
        /// <param name="image"></param>
        /// <param name="showTimestamp"></param>
        private void SetStatus(string text, Image image, bool showTimestamp)
        {
            lblStatus.Image = image;

            if (string.IsNullOrEmpty(text))
            {
                lblStatus.Text = string.Empty;
            }
            else
            {
                if (showTimestamp)
                {
                    lblStatus.Text = string.Format("{0} [{1:00}:{2:00}:{3:00}]", text, DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second);
                }
                else
                {
                    lblStatus.Text = text;
                }
            }

            statusMain.Refresh();
        }

        /// <summary>
        /// Loads the form information.
        /// </summary>
        private void LoadForm()
        {
            // Form settings
            this.Size = Settings.Default.FrmMain_Size;
            this.Location = Settings.Default.FrmMain_Position;
            this.WindowState = Settings.Default.FrmMain_WindowState;

            // Menu
            wrapTextMenuItem.Checked = Settings.Default.FrmMain_GridWrap;
            colorColumnsMenuItem.Checked = Settings.Default.FrmMain_GridColor;
            autosizeColumnsMenuItem.Checked = Settings.Default.FrmMain_GridAutosize;
            enableFiltersToolStripMenuItem.Checked = Settings.Default.FrmMain_FiltersEnabled;
        }

        /// <summary>
        /// Saves the form information.
        /// </summary>
        private void SaveForm()
        {
            Settings.Default.FrmMain_WindowState = this.WindowState;
            if (this.WindowState.Equals(FormWindowState.Minimized) || this.WindowState.Equals(FormWindowState.Maximized))
            {
                this.WindowState = FormWindowState.Normal;
            }

            Settings.Default.FrmMain_Size = this.Size;
            Settings.Default.FrmMain_Position = this.Location;

            Settings.Default.Save();
        }

        /// <summary>
        /// Loads the RESX group progress changed.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.ComponentModel.ProgressChangedEventArgs"/> instance containing the event data.</param>
        private void ProgressChanged(object sender, ProgressChangedEventArgs e)
        {
            tsProgressbar.Value = e.ProgressPercentage;
        }

        /// <summary>
        /// Executes all the gui setup that needs to be done when a processing is started
        /// </summary>
        /// <param name="affectsGrid">If set to <c>true</c> the grid events are disabled. They need to be re-enabled in the endjob</param>
        /// <param name="jobName">The string to display in the statusbar while processing.</param>
        /// <param name="jobImage">The icon to display in the statusbar while processing.</param>
        /// <param name="dataNeedsCleared">If set to <c>true</c> the grid's datasourse gets cleared.</param>
        private void StartJob(bool affectsGrid, string jobName, Image jobImage, bool dataNeedsCleared)
        {
            this.Busy = true;

            // Status
            this.SetStatus(jobName, jobImage, false);

            if (dataNeedsCleared)
            {
                this.ClearData();
            }

            if (affectsGrid)
            {
                // Events
                grdResx.RowsAdded -= new DataGridViewRowsAddedEventHandler(dataGridResx_RowsAdded);
                grdResx.RowsRemoved -= new DataGridViewRowsRemovedEventHandler(dataGridResx_RowsRemoved);

                grdResx.SuspendLayout();
            }
        }

        private void EndJob(bool affectedGrid, string jobDoneText, bool dataChanged)
        {
            if (affectedGrid)
            {
                grdResx.ResumeLayout();
                this.AutoSizeGridColumns();

                // Events
                grdResx.RowsAdded += new DataGridViewRowsAddedEventHandler(dataGridResx_RowsAdded);
                grdResx.RowsRemoved += new DataGridViewRowsRemovedEventHandler(dataGridResx_RowsRemoved);
            }

            this.Busy = false;
            this.NeedsSave = dataChanged;
            this.Enablings();

            this.SetStatus(jobDoneText, null, true);
        }
        #endregion Important methods

        #region Private Methods
        /// <summary>
        /// Gets the cultures from the loaded data
        /// Also include default value
        /// </summary>
        /// <param name="data">The datatable containing columns.</param>
        /// <returns>List of cultures in the grid</returns>
        private List<string> GetCultures(DataTable data)
        {
            List<string> cultures = new List<string>();

            if (data != null)
            {
                foreach (DataColumn c in data.Columns)
                {
                    if (!c.ColumnName.Equals(Enums.DataColumn.Nr, StringComparison.OrdinalIgnoreCase) &&
                        !c.ColumnName.Equals(Enums.DataColumn.Directory, StringComparison.OrdinalIgnoreCase) &&
                        !c.ColumnName.Equals(Enums.DataColumn.File, StringComparison.OrdinalIgnoreCase) &&
                        !c.ColumnName.Equals(Enums.DataColumn.Key, StringComparison.OrdinalIgnoreCase))
                    {
                        cultures.Add(c.ColumnName);
                    }
                }
            }

            return cultures;
        }

        /// <summary>
        /// Removes the rows that contain no value from the table
        /// </summary>
        /// <param name="data">The data that needs to be cleaned.</param>
        /// <returns>
        /// Table containing data without the empty rows
        /// </returns>
        private DataTable RemoveEmptyRows(DataTable data)
        {
            List<string> cultures = this.GetCultures(data);

            if (data.Rows.Count > 0)
            {
                int rowCount = data.Rows.Count - 1;

                for (int i = rowCount; i >= 0; i--)
                {
                    bool empty = true;

                    foreach (string colName in cultures)
                    {
                        if (data.Rows[i][colName].ToString().Trim().Length > 0)
                        {
                            empty = false;
                            break;
                        }
                    }

                    if (empty)
                    {
                        data.Rows[i].Delete();
                    }

                    int percent = 100 - (int)(((double)i / (double)rowCount) * 100);
                    bgWorkerMain.ReportProgress(percent);
                }

                data.AcceptChanges();
            }

            return data;
        }

        /// <summary>
        /// Finds the file in table.
        /// </summary>
        /// <param name="dir">The dir belonging to the file.</param>
        /// <param name="fileName">Name of the file.</param>
        /// <param name="fileTable">The file table.</param>
        /// <returns>True if dir and file are in the fileTable</returns>
        private bool FindFileInTable(string dir, string fileName, DataTable fileTable)
        {
            foreach (DataRow dr in fileTable.Rows)
            {
                if (dr[Enums.DataColumn.Directory].ToString().Equals(dir, StringComparison.OrdinalIgnoreCase) &&
                    dr[Enums.DataColumn.File].ToString().Equals(fileName, StringComparison.OrdinalIgnoreCase))
                {
                    return true;
                }
            }

            return false;
        }

        /// <summary>
        /// Finds a value in the grid.
        /// </summary>
        /// <param name="searchValue">The search value.</param>
        /// <param name="searchUp">if set to <c>true</c> [search up].</param>
        /// <param name="matchCase">if set to <c>true</c> [match case].</param>
        private void FindInGrid(string searchValue, bool searchUp, bool matchCase)
        {
            if (!searchUp)
            {
                #region Search up
                // Search in current row
                if (grdResx.CurrentCell.ColumnIndex + 1 < grdResx.ColumnCount)
                {
                    for (int i = grdResx.CurrentCell.ColumnIndex + 1; i < grdResx.ColumnCount; i++)
                    {
                        if (grdResx.Rows[grdResx.CurrentCell.RowIndex].Cells[i].Value != null)
                        {
                            if (matchCase)
                            {
                                if (grdResx.Rows[grdResx.CurrentCell.RowIndex].Cells[i].Value.ToString().Contains(searchValue))
                                {
                                    grdResx.CurrentCell = grdResx.Rows[grdResx.CurrentCell.RowIndex].Cells[i];

                                    return;
                                }
                            }
                            else
                            {
                                if (grdResx.Rows[grdResx.CurrentCell.RowIndex].Cells[i].Value.ToString().ToUpper().Contains(searchValue.ToUpper()))
                                {
                                    grdResx.CurrentCell = grdResx.Rows[grdResx.CurrentCell.RowIndex].Cells[i];

                                    return;
                                }
                            }
                        }
                    }
                }

                // Only search if not on last row
                if (grdResx.CurrentCell.RowIndex + 1 < grdResx.Rows.Count)
                {
                    for (int i = grdResx.CurrentCell.RowIndex + 1; i < grdResx.Rows.Count; i++)
                    {
                        for (int j = 0; j < grdResx.Rows[i].Cells.Count; j++)
                        {
                            if (grdResx.Rows[i].Cells[j].Value != null)
                            {
                                if (matchCase)
                                {
                                    if (grdResx.Rows[i].Cells[j].Value.ToString().Contains(searchValue))
                                    {
                                        grdResx.CurrentCell = grdResx.Rows[i].Cells[j];

                                        return;
                                    }
                                }
                                else
                                {
                                    if (grdResx.Rows[i].Cells[j].Value.ToString().ToUpper().Contains(searchValue.ToUpper()))
                                    {
                                        grdResx.CurrentCell = grdResx.Rows[i].Cells[j];

                                        return;
                                    }
                                }
                            }
                        }
                    }
                }
                #endregion Search up
            }
            else
            {
                #region Search down
                // Search in current row
                if (grdResx.CurrentCell.ColumnIndex > 0)
                {
                    for (int i = grdResx.CurrentCell.ColumnIndex - 1; i > 0; i--)
                    {
                        if (grdResx.Rows[grdResx.CurrentCell.RowIndex].Cells[i].Value != null)
                        {
                            if (matchCase)
                            {
                                if (grdResx.Rows[grdResx.CurrentCell.RowIndex].Cells[i].Value.ToString().Contains(searchValue))
                                {
                                    grdResx.CurrentCell = grdResx.Rows[grdResx.CurrentCell.RowIndex].Cells[i];

                                    return;
                                }
                            }
                            else
                            {
                                if (grdResx.Rows[grdResx.CurrentCell.RowIndex].Cells[i].Value.ToString().ToUpper().Contains(searchValue.ToUpper()))
                                {
                                    grdResx.CurrentCell = grdResx.Rows[grdResx.CurrentCell.RowIndex].Cells[i];

                                    return;
                                }
                            }
                        }
                    }
                }

                // Only search if not on last row
                if (grdResx.CurrentCell.RowIndex > 0)
                {
                    for (int i = grdResx.CurrentCell.RowIndex - 1; i >= 0; i--)
                    {
                        for (int j = grdResx.Rows[i].Cells.Count - 1; j >= 0; j--)
                        {
                            if (grdResx.Rows[i].Cells[j].Value != null)
                            {
                                if (matchCase)
                                {
                                    if (grdResx.Rows[i].Cells[j].Value.ToString().Contains(searchValue))
                                    {
                                        grdResx.CurrentCell = grdResx.Rows[i].Cells[j];

                                        return;
                                    }
                                }
                                else
                                {
                                    if (grdResx.Rows[i].Cells[j].Value.ToString().ToUpper().Contains(searchValue.ToUpper()))
                                    {
                                        grdResx.CurrentCell = grdResx.Rows[i].Cells[j];

                                        return;
                                    }
                                }
                            }
                        }
                    }
                }
                #endregion Search down
            }

            MessageBox.Show(this, string.Format("The search term '{0}' was not found.", searchValue), Settings.Default.AppName, MessageBoxButtons.OK, MessageBoxIcon.Information);
        }

        /// <summary>
        /// Autosizes the grid columns:
        /// First the columns are autosized by the grid, this method limits the maximum width of each column
        /// </summary>
        private void AutoSizeGridColumns()
        {
            if (autosizeColumnsMenuItem.Checked)
            {
                foreach (DataGridViewColumn c in grdResx.Columns)
                {
                    switch (c.Name)
                    {
                        case Enums.DataColumn.Nr:
                            c.Width = 50;
                            break;
                        case Enums.DataColumn.Directory:
                            c.Width = 50;
                            break;
                        case Enums.DataColumn.File:
                            c.Width = 50;
                            break;
                        case Enums.DataColumn.Key:
                            c.Width = 200;
                            break;
                        default:
                            c.Width = 400;
                            break;
                    }
                }
            }

            if (wrapTextMenuItem.Checked)
            {
                grdResx.DefaultCellStyle.WrapMode = DataGridViewTriState.True;
            }
            else
            {
                grdResx.DefaultCellStyle.WrapMode = DataGridViewTriState.False;
            }

            if (colorColumnsMenuItem.Checked)
            {
                foreach (DataGridViewColumn dgvc in grdResx.Columns)
                {
                    switch (dgvc.Name)
                    {
                        case Enums.DataColumn.Directory:
                            //dgvc.DefaultCellStyle.BackColor = Color.FromArgb(255, 192, 192);
                            //dgvc.DefaultCellStyle.BackColor = Color.FromArgb(60, 179, 221);

                            dgvc.DefaultCellStyle.ForeColor = Color.FromArgb(204, 19, 25);
                            break;

                        case Enums.DataColumn.File:
                            //dgvc.DefaultCellStyle.BackColor = Color.FromArgb(255, 192, 192);
                            //dgvc.DefaultCellStyle.BackColor = Color.FromArgb(55, 219, 71);
                            dgvc.DefaultCellStyle.ForeColor = Color.FromArgb(208, 117, 12);
                            break;

                        case Enums.DataColumn.Key:
                            //dgvc.DefaultCellStyle.BackColor = Color.FromArgb(255, 255, 192);
                            //dgvc.DefaultCellStyle.BackColor = Color.FromArgb(221, 151, 53);
                            dgvc.DefaultCellStyle.ForeColor = Color.FromArgb(14, 156, 206);
                            break;

                        case Enums.DataColumn.DefaultValue:
                            //dgvc.DefaultCellStyle.BackColor = Color.LightGreen;
                            //dgvc.DefaultCellStyle.BackColor = Color.FromArgb(184, 68, 231); 
                            //dgvc.DefaultCellStyle.Font = new Font("Microsoft Sans Serif", 9f, FontStyle.Bold);
                            dgvc.DefaultCellStyle.BackColor = Color.FromArgb(192, 255, 192);

                            break;

                        default:
                            //dgvc.DefaultCellStyle.BackColor = Color.FromArgb(192, 255, 192);
                            //dgvc.DefaultCellStyle.BackColor = Color.FromArgb(230, 66, );
                            //dgvc.DefaultCellStyle.BackColor = Color.FromArgb(192, 255, 192);
                            break;
                    }
                }
            }
            else
            {
                foreach (DataGridViewColumn dgvc in grdResx.Columns)
                {
                    dgvc.DefaultCellStyle.BackColor = Color.FromArgb(255, 255, 255);
                    dgvc.DefaultCellStyle.ForeColor = Color.FromArgb(0, 0, 0);
                }
            }
        }

        /// <summary>
        /// Backs up the current data.
        /// </summary>
        private void CreateUndo()
        {
            undoData = (DataTable)grdResx.DataSource;
        }

        /// <summary>
        /// Clears the data from memory        
        /// </summary>
        private void ClearData()
        {
            this.grdResx.DataSource = null;
            this.filteredData = null;
            this.lastLoadedInfo = null;
            this.undoData = null;
        }
        #endregion Private Methods

        #region Events
        #region Normal events
        /// <summary>
        /// Loads the registration info from the license file.
        /// </summary>
        private void LoadRegistrationInfo()
        {
            this.RegisterInfo = SerialMngr.Instance.ExtractRegistrationDataFromFile(Settings.Default.DataFilePath, false);
        }

        /// <summary>
        /// Handles the FormClosing event of the FrmMain control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.FormClosingEventArgs"/> instance containing the event data.</param>
        private void FrmMain_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (this.NeedsSave)
            {
                DialogResult decision = MessageBox.Show(this, "Do you want to save the changes you made?", Settings.Default.AppName, MessageBoxButtons.YesNoCancel, MessageBoxIcon.Question);

                if (decision == DialogResult.Cancel)
                {
                    e.Cancel = true;
                    return;
                }

                if (decision == DialogResult.Yes)
                {
                    e.Cancel = true;
                    this.closeAfterSaveFlag = true;
                    this.Save();
                }
            }

            this.SaveForm();
        }

        /// <summary>
        /// Handles the Click event of the exitToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void exitMenuItem_Click(object sender, EventArgs e)
        {
            this.Close();
        }

        /// <summary>
        /// Handles the Click event of the aboutToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void aboutMenuItem_Click(object sender, EventArgs e)
        {
            FrmAbout formAbout = new FrmAbout();
            formAbout.ShowDialog();
        }

        /// <summary>
        /// Handles the Click event of the resetSettingsToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void resetSettingsMenuItem_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show(this, "Are you sure you wish to reset settings to default?", Settings.Default.AppName, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
            {
                Settings.Default.FrmLoad_CsvPath = string.Empty;
                Settings.Default.FrmLoad_RbIndex = 0;
                Settings.Default.FrmLoad_ResxGroupDir = string.Empty;
                Settings.Default.FrmMain_Position = new Point(0, 0);
                Settings.Default.FrmMain_Size = new Size(800, 500);
                Settings.Default.FrmMain_WindowState = FormWindowState.Normal;
                Settings.Default.FrmSave_CsvPath = string.Empty;
                Settings.Default.FrmSave_RbIndex = 0;
                Settings.Default.FrmSave_ResxGroupDir = string.Empty;

                // Menu
                Settings.Default.FrmMain_GridColor = true;
                Settings.Default.FrmMain_GridAutosize = true;
                Settings.Default.FrmMain_GridWrap = true;
            }

            this.LoadForm();
        }

        /// <summary>
        /// Handles the Click event of the tsbLoad control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void tsbLoad_Click(object sender, EventArgs e)
        {
            loadMenuItem_Click(sender, e);
        }

        /// <summary>
        /// Handles the Click event of the tsbSaveAs control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void tsbSaveAs_Click(object sender, EventArgs e)
        {
            saveAsMenuItem_Click(sender, e);
        }

        /// <summary>
        /// Handles the Click event of the tsbClearData control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void tsbClearData_Click(object sender, EventArgs e)
        {
            removeAllRowsMenuItem_Click(sender, e);
        }

        /// <summary>
        /// Handles the Click event of the removeAllRowsToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void removeAllRowsMenuItem_Click(object sender, EventArgs e)
        {
            if (MessageBox.Show(this, "Are you sure you wish remove the loaded data?", Settings.Default.AppName, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
            {
                //this.Busy = true;

                //this.ClearData();
                //this.NeedsSave = false;
                //this.Enablings();

                this.StartJob(true, "Clearing data", Resources.Delete, true);
                this.EndJob(true, "Data cleared", false);
            }
        }

        /// <summary>
        /// Handles the Click event of the tsbRemoveFiles control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void tsbRemoveFiles_Click(object sender, EventArgs e)
        {
            removeFileMenuItem_Click(sender, e);
        }

        /// <summary>
        /// Handles the Click event of the tsbRemoveEmptyRows control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void tsbRemoveEmptyRows_Click(object sender, EventArgs e)
        {
            removeEmptyRowsMenuItem_Click(sender, e);
        }

        /// <summary>
        /// Handles the Click event of the tsbRemoveLanguageColumn control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void tsbRemoveLanguageColumn_Click(object sender, EventArgs e)
        {
            removeLanguageMenuItem_Click(sender, e);
        }

        /// <summary>
        /// Handles the Click event of the tsbRemoveKeys control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void tsbRemoveKeys_Click(object sender, EventArgs e)
        {
            removeKeysMenuItem_Click(sender, e);
        }

        /// <summary>
        /// Handles the Click event of the loadAdditionalToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void loadAdditionalMenuItem_Click(object sender, EventArgs e)
        {
            DataTable data = ((DataTable)grdResx.DataSource).Copy();

            if (data != null)
            {
                // ToDo: Get rid of duplicate code
                FrmLoad formLoad = new FrmLoad();
                formLoad.Mode = Enums.LoadModes.Additional;

                if (formLoad.ShowDialog() == DialogResult.OK)
                {
                    this.Busy = true;
                    this.SetStatus("Loading data...", Resources.Load, false);

                    DataTable newData = new DataTable("Data");
                    switch (formLoad.SelectedFormat)
                    {
                        case Utils.Enums.Format.ResxGroup:
                            newData = ResxMngr.Instance.LoadAllResxData(formLoad.ResxGroupInfo.Directory, formLoad.ResxGroupInfo.Recursive);
                            break;
                        case Utils.Enums.Format.Csv:
                            newData = CsvMngr.Instance.ReadCsv(formLoad.CsvFileInfo.Path);
                            break;
                        case Utils.Enums.Format.Resx:
                            newData = ResxMngr.Instance.LoadResxData(formLoad.ResxFileInfo.Path);
                            break;
                        case Utils.Enums.Format.Xml:
                            newData = XmlMngr.Instance.ReadXml(formLoad.XmlFileInfo.Path);
                            break;
                    }

                    if (newData != null)
                    {
                        List<string> newColumns = new List<string>();

                        bool compare = true;

                        // Verify if structure is the same
                        if (newData.Columns.Count != data.Columns.Count)
                        {
                            compare = false;
                        }
                        else
                        {
                            for (int i = 0; i < data.Columns.Count; i++)
                            {
                                if (!newData.Columns[i].ColumnName.Equals(data.Columns[i].ColumnName))
                                {
                                    compare = false;
                                    break;
                                }
                            }
                        }

                        if (compare)
                        {
                            foreach (DataRow dr in newData.Rows)
                            {
                                data.ImportRow(dr);
                            }
                        }
                        else
                        {
                            MessageBox.Show(this, "Data could not be loaded because the new data structure is not the same.", Settings.Default.AppName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                        }
                    }
                    else
                    {
                        MessageBox.Show(this, "Data could not be loaded.", Settings.Default.AppName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                    }
                }

                tsProgressbar.Value = 100;
                this.Refresh();

                grdResx.DataSource = data;
                if (grdResx.Columns.Contains(Enums.DataColumn.File))
                {
                    grdResx.Sort(grdResx.Columns[Enums.DataColumn.File], System.ComponentModel.ListSortDirection.Descending);
                }

                this.AutoSizeGridColumns();
                this.NeedsSave = true;

                this.Busy = false;
                this.Enablings();

                this.SetStatus("Additional data loaded", null, true);
            }
        }

        /// <summary>
        /// Handles the Click event of the tsbLoadAdditional control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void tsbLoadAdditional_Click(object sender, EventArgs e)
        {
            loadAdditionalMenuItem_Click(sender, e);
        }

        /// <summary>
        /// Handles the Click event of the removeLanguageToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void removeLanguageMenuItem_Click(object sender, EventArgs e)
        {
            DataTable data = (DataTable)grdResx.DataSource;

            if (data != null && data.Columns.Count > 0)
            {
                FrmSelectLang2Remove formSelectLang = new FrmSelectLang2Remove();
                formSelectLang.DataSource = (DataTable)grdResx.DataSource;

                if (formSelectLang.ShowDialog() == DialogResult.OK)
                {
                    this.Busy = true;
                    this.SetStatus("Saving undo data...", Resources.Undo, false);
                    this.CreateUndo();

                    this.SetStatus("Removing language(s)...", Resources.RemoveRow, false);

                    grdResx.DataSource = null;

                    for (int i = data.Columns.Count - 1; i >= 0; i--)
                    {
                        if (formSelectLang.SelectedLanguages.Contains(data.Columns[i].ColumnName))
                        {
                            data.Columns.Remove(data.Columns[i]);
                        }
                    }

                    grdResx.DataSource = data;
                    this.AutoSizeGridColumns();
                    this.Busy = false;
                    this.NeedsSave = true;
                    this.Enablings();
                }
            }
        }

        /// <summary>
        /// Handles the Click event of the toolStripButtonAddLanguage control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void toolStripButtonAddLanguage_Click(object sender, EventArgs e)
        {
            addLanguageMenuItem_Click(sender, e);
        }

        /// <summary>
        /// Handles the Click event of the autosizeColumnsToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void autosizeColumnsMenuItem_Click(object sender, EventArgs e)
        {
            autosizeColumnsMenuItem.Checked = !autosizeColumnsMenuItem.Checked;

            // Save changed settings
            Settings.Default.FrmMain_GridAutosize = autosizeColumnsMenuItem.Checked;

            this.AutoSizeGridColumns();
        }

        /// <summary>
        /// Handles the Click event of the colorColumnsToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void colorColumnsMenuItem_Click(object sender, EventArgs e)
        {
            colorColumnsMenuItem.Checked = !colorColumnsMenuItem.Checked;

            // Save changed settings
            Settings.Default.FrmMain_GridWrap = colorColumnsMenuItem.Checked;

            this.AutoSizeGridColumns();
        }

        /// <summary>
        /// Handles the Click event of the wrapTextToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void wrapTextMenuItem_Click(object sender, EventArgs e)
        {
            wrapTextMenuItem.Checked = !wrapTextMenuItem.Checked;

            // Save changed settings
            Settings.Default.FrmMain_GridWrap = wrapTextMenuItem.Checked;

            this.AutoSizeGridColumns();
        }

        /// <summary>
        /// Handles the Click event of the tsbSave control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void tsbSave_Click(object sender, EventArgs e)
        {
            saveMenuItem_Click(sender, e);
        }

        /// <summary>
        /// Handles the Click event of the findToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void findToolStripMenuItem_Click(object sender, EventArgs e)
        {
            FrmFind formFind = new FrmFind();
            formFind.FindStringHistory = lastFindStrings;

            if (formFind.ShowDialog() == DialogResult.OK)
            {
                lastFindStrings = IntegrateFindExpression(lastFindStrings, formFind.FindExpression);
                lastFindCaseSensitive = formFind.MatchCase;
                lastFindUpSetting = formFind.SearchUp;

                FindInGrid(formFind.FindExpression, formFind.SearchUp, formFind.MatchCase);

                this.Enablings();
            }
        }

        private List<string> IntegrateFindExpression(List<string> list, string item)
        {
            if (list != null)
            {
                if (list.Count > 0)
                {
                    bool found = false;
                    int counter = 0;

                    while (!found && counter < list.Count)
                    {
                        if (list[counter].Equals(item, StringComparison.OrdinalIgnoreCase))
                        {
                            found = true;
                        }
                        else
                        {
                            counter++;
                        }
                    }

                    if (found)
                    {
                        list.RemoveAt(counter);
                    }

                    list.Insert(0, item);
                }
                else
                {
                    list = new List<string>();
                    list.Add(item);
                }
            }
            else
            {
                list = new List<string>();
            }

            return list;
        }

        /// <summary>
        /// Handles the Click event of the tsbFind control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void tsbFind_Click(object sender, EventArgs e)
        {
            findToolStripMenuItem_Click(sender, e);
        }

        /// <summary>
        /// Handles the Click event of the tsbFindPrev control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void tsbFindPrev_Click(object sender, EventArgs e)
        {
            findPreviousToolStripMenuItem_Click(sender, e);
        }

        /// <summary>
        /// Handles the Click event of the findNextToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void findNextToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (lastFindStrings.Count > 0)
            {
                FindInGrid(lastFindStrings[0], false, lastFindCaseSensitive);
            }
        }

        /// <summary>
        /// Handles the Click event of the findPreviousToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void findPreviousToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (lastFindStrings.Count > 0)
            {
                FindInGrid(lastFindStrings[0], true, lastFindCaseSensitive);
            }
        }

        /// <summary>
        /// Handles the Click event of the tsbFindNext control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void tsbFindNext_Click(object sender, EventArgs e)
        {
            findNextToolStripMenuItem_Click(sender, e);
        }

        #region Grid events
        /// <summary>
        /// Handles the UserAddedRow event of the dataGridResx control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.DataGridViewRowEventArgs"/> instance containing the event data.</param>
        private void dataGridResx_UserAddedRow(object sender, DataGridViewRowEventArgs e)
        {
            DataTable loadedData = (DataTable)grdResx.DataSource;

            if (loadedData != null && loadedData.Columns.Contains(Utils.Enums.DataColumn.Nr))
            {
                int counter = 0;

                counter = DataMngr.Instance.MaxValueFromColumn(loadedData, Utils.Enums.DataColumn.Nr) + 1;

                int rowIndex = e.Row.Index - 1;

                if (rowIndex >= 0 && rowIndex < grdResx.RowCount)
                {
                    grdResx.Rows[rowIndex].Cells[Utils.Enums.DataColumn.Nr].Value = counter;
                }
            }

            this.Enablings();
        }

        /// <summary>
        /// Handles the UserDeletedRow event of the dataGridResx control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.DataGridViewRowEventArgs"/> instance containing the event data.</param>
        private void dataGridResx_UserDeletedRow(object sender, DataGridViewRowEventArgs e)
        {
            this.Enablings();
        }

        /// <summary>
        /// Handles the ColumnAdded event of the dataGridResx control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.DataGridViewColumnEventArgs"/> instance containing the event data.</param>
        private void dataGridResx_ColumnAdded(object sender, DataGridViewColumnEventArgs e)
        {
        }

        /// <summary>
        /// Handles the RowsRemoved event of the dataGridResx control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.DataGridViewRowsRemovedEventArgs"/> instance containing the event data.</param>
        private void dataGridResx_RowsRemoved(object sender, DataGridViewRowsRemovedEventArgs e)
        {
            this.Enablings();
        }

        /// <summary>
        /// Handles the RowsAdded event of the dataGridResx control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.DataGridViewRowsAddedEventArgs"/> instance containing the event data.</param>
        private void dataGridResx_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
        {
            ////this.Enablings();
        }

        /// <summary>
        /// Handles the CellEndEdit event of the grdResx control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.DataGridViewCellEventArgs"/> instance containing the event data.</param>
        private void grdResx_CellEndEdit(object sender, DataGridViewCellEventArgs e)
        {
            this.NeedsSave = true;
            this.Enablings();
        }

        /// <summary>
        /// Handles the DragEnter event of the grdResx control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.DragEventArgs"/> instance containing the event data.</param>
        private void grdResx_DragEnter(object sender, DragEventArgs e)
        {
            // Make sure they're actually dropping files (not text or anything else)
            if (e.Data.GetDataPresent(DataFormats.FileDrop, false))
            {
                // allow them to continue
                // (without this, the cursor stays a "NO" symbol
                e.Effect = DragDropEffects.All;
            }
        }

        /// <summary>
        /// Handles the DragDrop event of the grdResx control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.DragEventArgs"/> instance containing the event data.</param>
        private void grdResx_DragDrop(object sender, DragEventArgs e)
        {
            MessageBox.Show(this, "This functionality is not yet implemented.", Settings.Default.AppName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);

            ////string[] files = (string[])e.Data.GetData(DataFormats.FileDrop);
            ////foreach (string file in files)
            ////{
            ////    Console.WriteLine(file);
            ////}
        }

        /// <summary>
        /// Handles the DataError event of the grdResx control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.DataGridViewDataErrorEventArgs"/> instance containing the event data.</param>
        private void grdResx_DataError(object sender, DataGridViewDataErrorEventArgs e)
        {
            MessageBox.Show(this, "Invalid data entered. Please make sure the data is entered in the correct format. ", Settings.Default.AppName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);

            e.Cancel = true;
        }

        /// <summary>
        /// Handles the KeyDown event of the grdResx control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.Windows.Forms.KeyEventArgs"/> instance containing the event data.</param>
        private void grdResx_KeyDown(object sender, KeyEventArgs e)
        {
            if (e.Control && e.KeyCode == Keys.V && Clipboard.ContainsText())
            {
                grdResx.CurrentCell.Value = Clipboard.GetText(TextDataFormat.Text);
            }
        }
        #endregion Grid events
        #endregion Normal events

        #region Load data events
        /// <summary>
        /// Handles the Click event of the loadToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void loadMenuItem_Click(object sender, EventArgs e)
        {
            FrmLoad formLoad = new FrmLoad();
            formLoad.Mode = Enums.LoadModes.Data;

            if (formLoad.ShowDialog() == DialogResult.OK)
            {
                this.StartJob(true, "Loading data...", Resources.Load, true);

                object loadData = null;

                switch (formLoad.SelectedFormat)
                {
                    case Utils.Enums.Format.ResxGroup:
                        loadData = formLoad.ResxGroupInfo;
                        lastLoadedInfo = loadData;
                        break;

                    case Utils.Enums.Format.Resx:
                        loadData = formLoad.ResxFileInfo;

                        FileInfo fi = new FileInfo(formLoad.ResxFileInfo.Path);

                        if (fi.Directory == null)
                        {
                            log.ErrorFormat("Error getting fileinfo '{0}'", fi.ToString());
                            MessageBox.Show(this, "Could not load .Resx file, maybe path was incorrect.", Settings.Default.AppName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);

                            this.Busy = false;
                            this.NeedsSave = false;
                            this.Enablings();

                            return;
                        }

                        Utils.FileData.ResxGroupData modifiedData = new Utils.FileData.ResxGroupData(fi.Directory.FullName, true);

                        lastLoadedInfo = modifiedData;
                        break;

                    case Utils.Enums.Format.ResourceGroup:
                        loadData = formLoad.ResourceGroupInfo;
                        lastLoadedInfo = loadData;
                        break;

                    case Utils.Enums.Format.Resource:
                        loadData = formLoad.ResourceFileInfo;

                        FileInfo fiResource = new FileInfo(formLoad.ResourceFileInfo.Path);

                        if (fiResource.Directory == null)
                        {
                            log.ErrorFormat("Error getting fileinfo '{0}'", fiResource.ToString());
                            MessageBox.Show(this, "Could not load .Resource file, maybe path was incorrect.", Settings.Default.AppName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);

                            this.Busy = false;
                            this.NeedsSave = false;
                            this.Enablings();

                            return;
                        }

                        Utils.FileData.ResourceGroupData modifiedResourceData = new Utils.FileData.ResourceGroupData(fiResource.Directory.FullName, true);

                        lastLoadedInfo = modifiedResourceData;
                        break;

                    case Utils.Enums.Format.Csv:
                        loadData = formLoad.CsvFileInfo;
                        lastLoadedInfo = loadData;
                        break;

                    case Utils.Enums.Format.Xml:
                        loadData = formLoad.XmlFileInfo;
                        lastLoadedInfo = loadData;
                        break;

                    case Utils.Enums.Format.AndroidGroup:
                        loadData = formLoad.AndroidGroupInfo;
                        lastLoadedInfo = loadData;
                        break;

                    case Utils.Enums.Format.BlackberryGroup:
                        loadData = formLoad.BlackberryGroupInfo;
                        lastLoadedInfo = loadData;
                        break;
                }

                bgWorkerMain.DoWork += new DoWorkEventHandler(Event_LoadData);
                bgWorkerMain.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Event_LoadedData);
                bgWorkerMain.ProgressChanged += new ProgressChangedEventHandler(ProgressChanged);

                bgWorkerMain.RunWorkerAsync(loadData);
            }
        }

        /// <summary>
        /// Loads the RESX group.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.ComponentModel.DoWorkEventArgs"/> instance containing the event data.</param>
        private void Event_LoadData(object sender, DoWorkEventArgs e)
        {
            DataTable loadedData = new DataTable(InititalTableName);

            object loadInfo = e.Argument;
            string typeName = loadInfo.GetType().Name;

            bgWorkerMain.ReportProgress(100);

            switch (typeName.ToUpper())
            {
                case "RESXGROUPDATA":
                    Utils.FileData.ResxGroupData resxGroupData = (Utils.FileData.ResxGroupData)loadInfo;
                    loadedData = ResxMngr.Instance.LoadAllResxData(resxGroupData.Directory, resxGroupData.Recursive);
                    break;

                case "RESXDATA":
                    Utils.FileData.ResxData resxData = (Utils.FileData.ResxData)loadInfo;
                    loadedData = ResxMngr.Instance.LoadResxData(resxData.Path);
                    break;

                case "RESOURCEGROUPDATA":
                    Utils.FileData.ResourceGroupData resourceGroupData = (Utils.FileData.ResourceGroupData)loadInfo;
                    loadedData = ResourceMngr.Instance.LoadAllResourceData(resourceGroupData.Directory, resourceGroupData.Recursive);
                    break;

                case "RESOURCEDATA":
                    Utils.FileData.ResourceData resourceData = (Utils.FileData.ResourceData)loadInfo;
                    loadedData = ResourceMngr.Instance.LoadResourceData(resourceData.Path);
                    break;

                case "CSVDATA":
                    Utils.FileData.CsvData cvsData = (Utils.FileData.CsvData)loadInfo;
                    loadedData = CsvMngr.Instance.ReadCsv(cvsData.Path);
                    break;

                case "XMLDATA":
                    Utils.FileData.XmlData xmlData = (Utils.FileData.XmlData)loadInfo;
                    loadedData = XmlMngr.Instance.ReadXml(xmlData.Path);
                    break;

                case "ANDROIDGROUPDATA":
                    Utils.FileData.AndroidGroupData androidGroupData = (Utils.FileData.AndroidGroupData)loadInfo;
                    loadedData = AndroidManager.Instance.LoadAllAndroidTextResourceXml(androidGroupData.Directory, androidGroupData.Recursive);
                    break;

                case "BLACKBERRYGROUPDATA":
                    //Utils.FileData.BlackberryGroupData blackberryGroupData = (Utils.FileData.BlackberryGroupData)loadInfo;
                    //loadedData = Blackberryman.Instance.ReadXml(xmlData.Path);
                    break;
            }

            this.filteredData = null;
            if (enableFiltersToolStripMenuItem.Checked)
            {
                DataMngr.Instance.ApplyFiltersToDataTable(ref loadedData, ref filteredData, Settings.Default.FilterFilePath, Enums.DataColumn.File);
            }

            e.Result = loadedData;
        }

        /// <summary>
        /// Loads the RESX group.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.ComponentModel.RunWorkerCompletedEventArgs"/> instance containing the event data.</param>
        private void Event_LoadedData(object sender, RunWorkerCompletedEventArgs e)
        {
            this.SetStatus("Displaying data...", Resources.Load, false);
            grdResx.SuspendLayout();
            if (!bgWorkerMain.CancellationPending)
            {
                DataTable loadedData = (DataTable)e.Result;

                if (loadedData == null)
                {
                    MessageBox.Show(this,
                                    string.Concat("Data could not be loaded. This could be caused by the following reasons:",
                                                  Environment.NewLine,
                                                  "- You do not have access to some of the files",
                                                  Environment.NewLine,
                                                  "- Invalid path was supplied",
                                                  Environment.NewLine,
                                                  "- Other reasons"),
                                    Settings.Default.AppName,
                                    MessageBoxButtons.OK,
                                    MessageBoxIcon.Exclamation);
                }
                else
                {

                    grdResx.DataSource = loadedData;

                    if (grdResx.Columns.Contains(Enums.DataColumn.Nr))
                    {
                        grdResx.Sort(grdResx.Columns[Enums.DataColumn.Nr], System.ComponentModel.ListSortDirection.Ascending);
                    }
                    else
                    {
                        if (grdResx.Columns.Contains(Enums.DataColumn.Directory))
                        {
                            grdResx.Sort(grdResx.Columns[Enums.DataColumn.Directory], System.ComponentModel.ListSortDirection.Ascending);
                        }
                        else
                        {
                            if (grdResx.Columns.Contains(Enums.DataColumn.File))
                            {
                                grdResx.Sort(grdResx.Columns[Enums.DataColumn.File], System.ComponentModel.ListSortDirection.Ascending);
                            }
                        }
                    }
                }
            }
            else
            {
                this.SetStatus("Load cancelled", null, true);
            }

            this.EndJob(true, "Data successfully loaded", false);
            grdResx.ResumeLayout();

            bgWorkerMain.DoWork -= new DoWorkEventHandler(Event_LoadData);
            bgWorkerMain.RunWorkerCompleted -= new RunWorkerCompletedEventHandler(Event_LoadedData);
            bgWorkerMain.ProgressChanged -= new ProgressChangedEventHandler(ProgressChanged);
        }
        #endregion Load data events

        #region Save events
        /// <summary>
        /// Handles the Click event of the saveToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void saveMenuItem_Click(object sender, EventArgs e)
        {
            this.Save();
        }

        private void Save()
        {
            if (lastLoadedInfo != null)
            {
                this.Busy = true;
                this.SetStatus("Saving...", Resources.Save, false);

                bgWorkerMain.DoWork += new DoWorkEventHandler(Event_SaveAs);
                bgWorkerMain.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Event_SavedAs);
                bgWorkerMain.ProgressChanged += new ProgressChangedEventHandler(ProgressChanged);

                bgWorkerMain.RunWorkerAsync(lastLoadedInfo);
            }
        }

        /// <summary>
        /// Handles the Click event of the saveAsToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void saveAsMenuItem_Click(object sender, EventArgs e)
        {
            FrmSave formSave = new FrmSave();

            if (formSave.ShowDialog() == DialogResult.OK)
            {
                this.Busy = true;
                this.SetStatus("Saving...", Resources.SaveAs, false);

                object saveData = null;

                switch (formSave.SelectedFormat)
                {
                    case Utils.Enums.Format.ResxGroup:
                        saveData = formSave.ResxGroupInfo;
                        break;

                    case Utils.Enums.Format.ResourceGroup:
                        saveData = formSave.ResourceGroupInfo;
                        break;

                    case Utils.Enums.Format.Csv:
                        saveData = formSave.CsvFileData;
                        break;

                    case Utils.Enums.Format.Xml:
                        saveData = formSave.XmlFileData;
                        break;

                    case Utils.Enums.Format.AndroidGroup:
                        saveData = formSave.AndroidGroupInfo;
                        break;

                    case Utils.Enums.Format.BlackberryGroup:
                        saveData = formSave.BlackberryGroupInfo;
                        break;
                }

                // Save the load data for later
                lastLoadedInfo = saveData;

                bgWorkerMain.DoWork += new DoWorkEventHandler(Event_SaveAs);
                bgWorkerMain.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Event_SavedAs);
                bgWorkerMain.ProgressChanged += new ProgressChangedEventHandler(ProgressChanged);

                bgWorkerMain.RunWorkerAsync(saveData);
            }
        }

        /// <summary>
        /// Data save as event of the background worker
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.ComponentModel.DoWorkEventArgs"/> instance containing the event data.</param>
        private void Event_SaveAs(object sender, DoWorkEventArgs e)
        {
            object saveInfo = e.Argument;
            string typeName = saveInfo.GetType().Name;
            bool success = false;

            if (grdResx.DataSource != null)
            {
                DataTable table = ((DataTable)grdResx.DataSource).Copy();
                table.AcceptChanges();

                switch (typeName.ToUpper())
                {
                    case "RESXGROUPDATA":
                    case "RESXDATA":
                        Utils.FileData.ResxGroupData resxGroupData = (Utils.FileData.ResxGroupData)saveInfo;
                        success = ResxMngr.Instance.SaveResxGroup(table, resxGroupData.Directory);
                        break;

                    case "RESOURCEGROUPDATA":
                    case "RESOURCEDATA":
                        Utils.FileData.ResourceGroupData resourceGroupData = (Utils.FileData.ResourceGroupData)saveInfo;
                        success = ResourceMngr.Instance.SaveResourceGroup(table, resourceGroupData.Directory);
                        break;

                    case "CSVDATA":
                        Utils.FileData.CsvData cvsData = (Utils.FileData.CsvData)saveInfo;
                        success = CsvMngr.Instance.SaveCsv(table, cvsData.Path, Enums.CvsSeparators[cvsData.SeparatorIndex], cvsData.IncludeHeaders, Settings.Default.MaxNrCvsRowsToScan);
                        break;

                    case "XMLDATA":
                        Utils.FileData.XmlData xmlData = (Utils.FileData.XmlData)saveInfo;
                        success = XmlMngr.Instance.SaveXml(table, xmlData.Path);
                        break;

                    case "ANDROIDGROUPDATA":
                    case "ANDROIDDATA":
                        Utils.FileData.AndroidGroupData androidGroupData = (Utils.FileData.AndroidGroupData)saveInfo;
                        success = AndroidManager.Instance.SaveAndroidGroup(table, androidGroupData.Directory);
                        break;

                    case "BLACKBERRYGROUPDATA":
                    case "BLACKBERRYDATA":
                        Utils.FileData.BlackberryGroupData blackberryGroupData = (Utils.FileData.BlackberryGroupData)saveInfo;
                        //success = blackberryManager.Instance.SaveAndroidGroup(table, androidGroupData.Directory);
                        break;
                }
            }

            bgWorkerMain.ReportProgress(100);

            e.Result = success;
        }

        /// <summary>
        /// Data saved as event of the background worker
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.ComponentModel.RunWorkerCompletedEventArgs"/> instance containing the event data.</param>
        private void Event_SavedAs(object sender, RunWorkerCompletedEventArgs e)
        {
            if (!bgWorkerMain.CancellationPending)
            {
                bool result = (bool)e.Result;

                if (!result)
                {
                    MessageBox.Show(this, "Data was NOT saved. Maybe some necessary columns are missing", Settings.Default.AppName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }
            }
            else
            {
                MessageBox.Show(this, "Save canceled", Settings.Default.AppName, MessageBoxButtons.OK, MessageBoxIcon.Information);
            }

            bgWorkerMain.DoWork -= new DoWorkEventHandler(Event_SaveAs);
            bgWorkerMain.RunWorkerCompleted -= new RunWorkerCompletedEventHandler(Event_SavedAs);
            bgWorkerMain.ProgressChanged -= new ProgressChangedEventHandler(ProgressChanged);

            this.Busy = false;
            this.NeedsSave = false;
            this.Enablings();

            this.SetStatus("Data saved successfully", null, true);

            if (this.closeAfterSaveFlag)
            {
                this.Close();
            }
        }
        #endregion Save events

        #region Removed empty rows events
        /// <summary>
        /// Handles the Click event of the removeEmptyRowsToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void removeEmptyRowsMenuItem_Click(object sender, EventArgs e)
        {
            this.CreateUndo();

            DataTable data = (DataTable)grdResx.DataSource;

            if (data != null)
            {
                if (MessageBox.Show(this, "Are you sure you wish remove the empty rows (ones that do not have value in any language)?", Settings.Default.AppName, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                {
                    this.Busy = true;

                    this.SetStatus("Removing empty rows...", Resources.RemoveEmptyRow, false);

                    grdResx.DataSource = null;

                    bgWorkerMain.DoWork += new DoWorkEventHandler(Event_RemoveEmptyRows);
                    bgWorkerMain.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Event_RemovedEmptyRows);
                    bgWorkerMain.ProgressChanged += new ProgressChangedEventHandler(ProgressChanged);

                    bgWorkerMain.RunWorkerAsync(data);
                }
            }
        }

        /// <summary>
        /// Removes the empty rows event.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.ComponentModel.DoWorkEventArgs"/> instance containing the event data.</param>
        private void Event_RemoveEmptyRows(object sender, DoWorkEventArgs e)
        {
            DataTable data = (DataTable)e.Argument;

            DataTable result = this.RemoveEmptyRows(data);
            e.Result = result;
        }

        /// <summary>
        /// Removeds the empty rows event.
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.ComponentModel.RunWorkerCompletedEventArgs"/> instance containing the event data.</param>
        private void Event_RemovedEmptyRows(object sender, RunWorkerCompletedEventArgs e)
        {
            grdResx.DataSource = (DataTable)e.Result;

            bgWorkerMain.DoWork -= new DoWorkEventHandler(Event_RemoveEmptyRows);
            bgWorkerMain.RunWorkerCompleted -= new RunWorkerCompletedEventHandler(Event_RemovedEmptyRows);
            bgWorkerMain.ProgressChanged -= new ProgressChangedEventHandler(ProgressChanged);

            this.AutoSizeGridColumns();
            this.Busy = false;
            this.NeedsSave = true;
            Enablings();
        }
        #endregion Removed empty rows events

        #region Remove File Events
        /// <summary>
        /// Handles the Click event of the removeFileToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void removeFileMenuItem_Click(object sender, EventArgs e)
        {
            if (grdResx.DataSource != null)
            {
                FrmSelectFiles formSelectFiles = new FrmSelectFiles();
                formSelectFiles.FileTable = ((DataTable)grdResx.DataSource).Copy();

                if (formSelectFiles.ShowDialog() == DialogResult.OK)
                {
                    this.SetStatus("Saving undo data...", Resources.Undo, false);
                    this.CreateUndo();

                    this.Busy = true;
                    this.SetStatus("Removing files...", Resources.RemoveRow, false);

                    tempTable = (DataTable)grdResx.DataSource;
                    grdResx.DataSource = null;

                    bgWorkerMain.DoWork += new DoWorkEventHandler(Event_RemoveFile);
                    bgWorkerMain.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Event_RemovedFile);
                    bgWorkerMain.ProgressChanged += new ProgressChangedEventHandler(ProgressChanged);

                    bgWorkerMain.RunWorkerAsync(formSelectFiles.SelectedFileTable);
                }
            }
        }

        /// <summary>
        /// Handles the RemoveFile event of the Event control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.DoWorkEventArgs"/> instance containing the event data.</param>
        private void Event_RemoveFile(object sender, DoWorkEventArgs e)
        {
            DataTable tableSelectedFiles = (DataTable)e.Argument;

            if (tempTable.Rows.Count > 0 && tableSelectedFiles.Rows.Count > 0)
            {
                grdResx.DataSource = null;

                int rowCount = tempTable.Rows.Count - 1;
                int percent = 0;

                for (int i = rowCount; i >= 0; i--)
                {
                    if (this.FindFileInTable(tempTable.Rows[i][Enums.DataColumn.Directory].ToString(), tempTable.Rows[i][Enums.DataColumn.File].ToString(), tableSelectedFiles))
                    {
                        tempTable.Rows[i].Delete();
                    }

                    percent = 100 - (int)(((double)i / (double)rowCount) * 100);
                    bgWorkerMain.ReportProgress(percent);
                }

                tempTable.AcceptChanges();
            }

            e.Result = tempTable.Copy();
            tempTable = null;
        }

        /// <summary>
        /// Handles the RemovedFile event of the Event control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.RunWorkerCompletedEventArgs"/> instance containing the event data.</param>
        private void Event_RemovedFile(object sender, RunWorkerCompletedEventArgs e)
        {
            DataTable data = (DataTable)e.Result;
            grdResx.DataSource = data;
            this.AutoSizeGridColumns();

            bgWorkerMain.DoWork -= new DoWorkEventHandler(Event_RemoveFile);
            bgWorkerMain.RunWorkerCompleted -= new RunWorkerCompletedEventHandler(Event_RemovedFile);
            bgWorkerMain.ProgressChanged -= new ProgressChangedEventHandler(ProgressChanged);

            this.Busy = false;
            this.NeedsSave = true;
            Enablings();
        }
        #endregion Remove File Events

        #region Remove keys
        /// <summary>
        /// Handles the Click event of the removeKeysToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void removeKeysMenuItem_Click(object sender, EventArgs e)
        {
            DataTable data = (DataTable)grdResx.DataSource;

            if (data != null && data.Rows.Count > 0)
            {
                FrmSelectKeys formSelectKeys = new FrmSelectKeys();
                formSelectKeys.KeysTable = data.Copy();

                if (formSelectKeys.ShowDialog() == DialogResult.OK)
                {
                    this.Busy = true;
                    this.SetStatus("Saving undo data...", Resources.Undo, false);
                    this.CreateUndo();

                    this.SetStatus("Removing keys...", Resources.RemoveKey, false);

                    tempTable = (DataTable)grdResx.DataSource;
                    grdResx.DataSource = null;

                    bgWorkerMain.DoWork += new DoWorkEventHandler(Event_RemoveKeys);
                    bgWorkerMain.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Event_RemovedKeys);
                    bgWorkerMain.ProgressChanged += new ProgressChangedEventHandler(ProgressChanged);

                    bgWorkerMain.RunWorkerAsync(formSelectKeys.SelectedKeys);
                }
            }
        }

        /// <summary>
        /// Handles the RemoveKeys event of the Event control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.DoWorkEventArgs"/> instance containing the event data.</param>
        private void Event_RemoveKeys(object sender, DoWorkEventArgs e)
        {
            List<string> listKeys = (List<string>)e.Argument;

            int rowNum = tempTable.Rows.Count - 1;
            int percent = 0;
            for (int i = rowNum; i >= 0; i--)
            {
                if (listKeys.Contains(ResxMngr.Instance.GetKey(tempTable.Rows[i][Enums.DataColumn.Key].ToString())))
                {
                    tempTable.Rows[i].Delete();
                }

                percent = 100 - (int)(((double)i / (double)rowNum) * 100);
                bgWorkerMain.ReportProgress(percent);
            }

            tempTable.AcceptChanges();

            e.Result = tempTable.Copy();
            tempTable = null;
        }

        /// <summary>
        /// Handles the RemovedKeys event of the Event control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.RunWorkerCompletedEventArgs"/> instance containing the event data.</param>
        private void Event_RemovedKeys(object sender, RunWorkerCompletedEventArgs e)
        {
            DataTable data = (DataTable)e.Result;
            grdResx.DataSource = data;
            this.AutoSizeGridColumns();

            bgWorkerMain.DoWork -= new DoWorkEventHandler(Event_RemoveKeys);
            bgWorkerMain.RunWorkerCompleted -= new RunWorkerCompletedEventHandler(Event_RemovedKeys);
            bgWorkerMain.ProgressChanged -= new ProgressChangedEventHandler(ProgressChanged);

            this.Busy = false;
            this.NeedsSave = true;
            Enablings();
        }
        #endregion Remove keys

        #region Add language events
        /// <summary>
        /// Handles the Click event of the addLanguageToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void addLanguageMenuItem_Click(object sender, EventArgs e)
        {
            tempTable = (DataTable)grdResx.DataSource;

            if (tempTable != null)
            {
                FrmSelectLang2Add formAddLang = new FrmSelectLang2Add();
                formAddLang.DataSource = tempTable;

                if (formAddLang.ShowDialog() == DialogResult.OK)
                {
                    this.Busy = true;
                    this.SetStatus("Saving undo data...", Resources.Undo, false);
                    this.CreateUndo();

                    this.SetStatus("Adding language(s)...", Resources.AddColumn, false);

                    grdResx.DataSource = null;

                    foreach (string language in formAddLang.SelectedLanguages)
                    {
                        if (!tempTable.Columns.Contains(language))
                        {
                            tempTable.Columns.Add(language);
                        }
                        else
                        {
                            // ToDo: do someting about the languages that were selected but already exist
                            ////formAddLang.SelectedLanguages.Remove(language);
                        }
                    }

                    // Copy default values if needed
                    if (formAddLang.CopyDefaultValues && tempTable.Columns.Contains(Enums.DataColumn.DefaultValue))
                    {
                        this.SetStatus("Copying default values...", Resources.CopyDefaultValues, false);

                        bgWorkerMain.DoWork += new DoWorkEventHandler(Event_CopyDefaultValues);
                        bgWorkerMain.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Event_CopiedDefaultValues);
                        bgWorkerMain.ProgressChanged += new ProgressChangedEventHandler(ProgressChanged);

                        bgWorkerMain.RunWorkerAsync(formAddLang.SelectedLanguages);
                    }
                    else
                    {
                        grdResx.DataSource = tempTable;
                        this.AutoSizeGridColumns();
                        this.Busy = false;
                        this.NeedsSave = true;
                        Enablings();
                    }
                }
            }
        }

        /// <summary>
        /// Handles the CopyDefaultValues event of the Event control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.DoWorkEventArgs"/> instance containing the event data.</param>
        private void Event_CopyDefaultValues(object sender, DoWorkEventArgs e)
        {
            List<string> selectedLanguages = (List<string>)e.Argument;

            int rowCount = tempTable.Rows.Count;
            int counter = 0;
            int percent = 0;

            foreach (DataRow dr in tempTable.Rows)
            {
                foreach (string language in selectedLanguages)
                {
                    dr[language] = dr[Enums.DataColumn.DefaultValue].ToString();
                }

                counter++;

                percent = (int)(((double)counter / (double)rowCount) * 100);
                bgWorkerMain.ReportProgress(percent);
            }

            e.Result = tempTable.Copy();
            tempTable = null;
        }

        /// <summary>
        /// Handles the CopiedDefaultValues event of the Event control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.RunWorkerCompletedEventArgs"/> instance containing the event data.</param>
        private void Event_CopiedDefaultValues(object sender, RunWorkerCompletedEventArgs e)
        {
            this.SetStatus("Displaying data...", Resources.CopyDefaultValues, false);

            grdResx.DataSource = (DataTable)e.Result;
            this.AutoSizeGridColumns();

            bgWorkerMain.DoWork -= new DoWorkEventHandler(Event_CopyDefaultValues);
            bgWorkerMain.RunWorkerCompleted -= new RunWorkerCompletedEventHandler(Event_CopiedDefaultValues);
            bgWorkerMain.ProgressChanged -= new ProgressChangedEventHandler(ProgressChanged);

            this.Busy = false;
            this.NeedsSave = true;
            Enablings();
        }
        #endregion Add language events

        #region Copy default values
        /// <summary>
        /// Handles the Click event of the copyDefaultValueToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void copyDefaultValueToolStripMenuItem_Click(object sender, EventArgs e)
        {
            FrmCopyDefaultValueOptions fCopyOptions = new FrmCopyDefaultValueOptions();
            fCopyOptions.CopyOptions = this.CopyDefValueOptions;

            if (fCopyOptions.ShowDialog() == DialogResult.OK)
            {
                this.CopyDefValueOptions = fCopyOptions.CopyOptions;

                DataTable data = (DataTable)grdResx.DataSource;
                data.AcceptChanges();

                if (data != null && data.Rows.Count > 0)
                {
                    //if (MessageBox.Show(this, "Are you sure you wish to copy 'Default values' column values to empty language column cells?", Settings.Default.AppName, MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                    //{
                    this.Busy = true;

                    this.SetStatus("Saving undo data...", Resources.Undo, false);
                    this.CreateUndo();

                    this.SetStatus("Copying default values...", Resources.CopyDefaultValues, false);

                    grdResx.DataSource = null;

                    bgWorkerMain.DoWork += new DoWorkEventHandler(Event_CopyDefaultValues2EmptyCells);
                    bgWorkerMain.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Event_CopiedDefaultValues2EmptyCells);
                    bgWorkerMain.ProgressChanged += new ProgressChangedEventHandler(ProgressChanged);

                    bgWorkerMain.RunWorkerAsync(data);
                    //}
                }
            }
        }

        /// <summary>
        /// Handles the CopyDefaultValues2EmptyCells event
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.DoWorkEventArgs"/> instance containing the event data.</param>
        private void Event_CopyDefaultValues2EmptyCells(object sender, DoWorkEventArgs e)
        {
            DataTable data = (DataTable)e.Argument;

            if (data != null)
            {
                string prefix = string.Empty;
                string suffix = string.Empty;

                if (this.CopyDefValueOptions != null)
                {
                    prefix = this.CopyDefValueOptions.Prefix;
                    suffix = this.CopyDefValueOptions.Suffix;
                }

                List<string> cultures = GetCultures(data);

                int percent = 0;

                for (int i = 0; i < data.Rows.Count; i++)
                {
                    foreach (string culture in cultures)
                    {
                        if (data.Rows[i][culture].ToString().Length == 0)
                        {
                            if (!culture.Equals(Enums.DataColumn.DefaultValue, StringComparison.OrdinalIgnoreCase))
                            {
                                data.Rows[i][culture] = string.Concat(prefix, data.Rows[i][Enums.DataColumn.DefaultValue].ToString(), suffix);
                            }
                        }
                    }

                    percent = 100 - (int)(((double)i / (double)data.Rows.Count) * 100);
                    bgWorkerMain.ReportProgress(percent);
                }
            }

            e.Result = data;
        }

        /// <summary>
        /// Handles the CopiedDefaultValues2EmptyCells event
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.RunWorkerCompletedEventArgs"/> instance containing the event data.</param>
        private void Event_CopiedDefaultValues2EmptyCells(object sender, RunWorkerCompletedEventArgs e)
        {
            DataTable data = (DataTable)e.Result;
            grdResx.DataSource = data;
            this.AutoSizeGridColumns();

            bgWorkerMain.DoWork -= new DoWorkEventHandler(Event_CopyDefaultValues2EmptyCells);
            bgWorkerMain.RunWorkerCompleted -= new RunWorkerCompletedEventHandler(Event_CopiedDefaultValues2EmptyCells);
            bgWorkerMain.ProgressChanged -= new ProgressChangedEventHandler(ProgressChanged);

            this.Busy = false;
            this.NeedsSave = true;
            Enablings();
        }
        #endregion Copy default values

        #region Undo
        /// <summary>
        /// Handles the Click event of the undoToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void undoToolStripMenuItem_Click(object sender, EventArgs e)
        {
            grdResx.DataSource = undoData.Copy();
            undoData = null;
            this.NeedsSave = true;
            this.Enablings();
        }

        /// <summary>
        /// Handles the Click event of the tsbUndo control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void tsbUndo_Click(object sender, EventArgs e)
        {
            undoToolStripMenuItem_Click(sender, e);
        }
        #endregion Undo

        #region License
        /// <summary>
        /// Click event of the activate license menu item
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void activateLicenseToolStripMenuItem_Click(object sender, EventArgs e)
        {
            FrmActivateLicense fActivateLic = new FrmActivateLicense();

            fActivateLic.ShowDialog();
        }
        #endregion License

        #region Filters
        /// <summary>
        /// Handles the Click event of the filtersToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void filtersToolStripMenuItem_Click(object sender, EventArgs e)
        {
            FrmFilters filtersForm = new FrmFilters();

            filtersForm.ShowDialog();

            #region Reload data/Apply filter
            this.ApplyFilters();
            #endregion Reload data/Apply filter
        }

        /// <summary>
        /// Handles the Click event of the enableFiltersToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void enableFiltersToolStripMenuItem_Click(object sender, EventArgs e)
        {
            enableFiltersToolStripMenuItem.Checked = !enableFiltersToolStripMenuItem.Checked;
            tsbFiltersEnabled.Checked = enableFiltersToolStripMenuItem.Checked;

            // Save changed settings
            Settings.Default.FrmMain_FiltersEnabled = enableFiltersToolStripMenuItem.Checked;

            this.ApplyFilters();
        }

        /// <summary>
        /// Handles the Click event of the tsbFiltersEnabled control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void tsbFiltersEnabled_Click(object sender, EventArgs e)
        {
            enableFiltersToolStripMenuItem_Click(sender, e);
        }

        /// <summary>
        /// Applies the filters based on the selection/checked state of the enable filters menu item.
        /// </summary>
        private void ApplyFilters()
        {
            this.StartJob(true, "Adjusting filters", Resources.Funnel, false);

            //Reload the data
            string filterPath = enableFiltersToolStripMenuItem.Checked ? Settings.Default.FilterFilePath : string.Empty;
            DataTable loadedData = (DataTable)grdResx.DataSource;
            DataMngr.Instance.ApplyFiltersToDataTable(ref loadedData, ref filteredData, filterPath, Enums.DataColumn.File);

            bool isThereDataLoaded = (grdResx.DataSource == null);
            this.EndJob(true, enableFiltersToolStripMenuItem.Checked ? "Filters turned on" : "Filters turned off", !isThereDataLoaded);
        }
        #endregion Filters

        #region Load Patch
        /// <summary>
        /// Handles the Click event of the loadPatchToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void loadPatchToolStripMenuItem_Click(object sender, EventArgs e)
        {
            FrmLoad formLoad = new FrmLoad();
            formLoad.Mode = Enums.LoadModes.Patch;

            if (formLoad.ShowDialog() == DialogResult.OK)
            {
                //this.Busy = true;
                //this.SetStatus("Patching data...", Resources.LoadPatch, false);
                this.StartJob(true, "Patching data...", Resources.LoadPatch, false);

                object loadData = null;

                switch (formLoad.SelectedFormat)
                {
                    case Utils.Enums.Format.ResxGroup:
                        loadData = formLoad.ResxGroupInfo;
                        //lastLoadedInfo = loadData;
                        break;

                    case Utils.Enums.Format.Resx:
                        loadData = formLoad.ResxFileInfo;

                        FileInfo fi = new FileInfo(formLoad.ResxFileInfo.Path);

                        if (fi.Directory == null)
                        {
                            log.ErrorFormat("Error getting fileinfo '{0}'", fi.ToString());
                            MessageBox.Show(this, "Could not load .Resx file, maybe path was incorrect.", Settings.Default.AppName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);

                            this.Busy = false;
                            this.NeedsSave = false;
                            this.Enablings();

                            return;
                        }

                        Utils.FileData.ResxGroupData modifiedData = new Utils.FileData.ResxGroupData(fi.Directory.FullName, true);

                        //lastLoadedInfo = modifiedData;
                        break;

                    case Utils.Enums.Format.ResourceGroup:
                        loadData = formLoad.ResourceGroupInfo;
                        //lastLoadedInfo = loadData;
                        break;

                    case Utils.Enums.Format.Resource:
                        loadData = formLoad.ResourceFileInfo;

                        FileInfo fiResource = new FileInfo(formLoad.ResourceFileInfo.Path);

                        if (fiResource.Directory == null)
                        {
                            log.ErrorFormat("Error getting fileinfo '{0}'", fiResource.ToString());
                            MessageBox.Show(this, "Could not load .Resource file, maybe path was incorrect.", Settings.Default.AppName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);

                            this.Busy = false;
                            this.NeedsSave = false;
                            this.Enablings();

                            return;
                        }

                        Utils.FileData.ResourceGroupData modifiedResourceData = new Utils.FileData.ResourceGroupData(fiResource.Directory.FullName, true);

                        //lastLoadedInfo = modifiedResourceData;
                        break;

                    case Utils.Enums.Format.Csv:
                        loadData = formLoad.CsvFileInfo;
                        //lastLoadedInfo = loadData;
                        break;

                    case Utils.Enums.Format.Xml:
                        loadData = formLoad.XmlFileInfo;
                        //lastLoadedInfo = loadData;
                        break;
                }

                bgWorkerMain.DoWork += new DoWorkEventHandler(Event_LoadPatchData);
                bgWorkerMain.RunWorkerCompleted += new RunWorkerCompletedEventHandler(Event_LoadedPatchData);
                bgWorkerMain.ProgressChanged += new ProgressChangedEventHandler(ProgressChanged);
                //grdResx.RowsAdded -= new DataGridViewRowsAddedEventHandler(dataGridResx_RowsAdded);

                bgWorkerMain.RunWorkerAsync(loadData);
            }
        }

        /// <summary>
        /// Loads the patch data.
        /// This is the operation that can take a long time
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.ComponentModel.DoWorkEventArgs"/> instance containing the event data.</param>
        private void Event_LoadPatchData(object sender, DoWorkEventArgs e)
        {
            DataTable loadedData = new DataTable(InititalTableName);

            object loadInfo = e.Argument;
            string typeName = loadInfo.GetType().Name;

            switch (typeName.ToUpper())
            {
                case "RESXGROUPDATA":
                    Utils.FileData.ResxGroupData resxGroupData = (Utils.FileData.ResxGroupData)loadInfo;
                    loadedData = ResxMngr.Instance.LoadAllResxData(resxGroupData.Directory, resxGroupData.Recursive);
                    break;

                case "RESXDATA":
                    Utils.FileData.ResxData resxData = (Utils.FileData.ResxData)loadInfo;
                    loadedData = ResxMngr.Instance.LoadResxData(resxData.Path);
                    break;

                case "RESOURCEGROUPDATA":
                    Utils.FileData.ResourceGroupData resourceGroupData = (Utils.FileData.ResourceGroupData)loadInfo;
                    loadedData = ResourceMngr.Instance.LoadAllResourceData(resourceGroupData.Directory, resourceGroupData.Recursive);
                    break;

                case "RESOURCEDATA":
                    Utils.FileData.ResourceData resourceData = (Utils.FileData.ResourceData)loadInfo;
                    loadedData = ResourceMngr.Instance.LoadResourceData(resourceData.Path);
                    break;

                case "CSVDATA":
                    Utils.FileData.CsvData cvsData = (Utils.FileData.CsvData)loadInfo;
                    loadedData = CsvMngr.Instance.ReadCsv(cvsData.Path);
                    break;

                case "XMLDATA":
                    Utils.FileData.XmlData xmlData = (Utils.FileData.XmlData)loadInfo;
                    loadedData = XmlMngr.Instance.ReadXml(xmlData.Path);
                    break;
            }

            DataTable alreadyLoadedData = null;

            if (loadedData != null)
            {
                // Reset the filter
                alreadyLoadedData = (DataTable)grdResx.DataSource;

                DataMngr.Instance.ApplyFiltersToDataTable(ref alreadyLoadedData, ref filteredData, string.Empty, Enums.DataColumn.File);

                // Merge the data
                List<string> keyColumns = new List<string>();
                keyColumns.Add(Enums.DataColumn.Directory);
                keyColumns.Add(Enums.DataColumn.File);
                keyColumns.Add(Enums.DataColumn.Key);

                List<string> notImportantColumns = new List<string>();
                notImportantColumns.Add(Enums.DataColumn.DefaultValue);
                notImportantColumns.Add(Enums.DataColumn.Nr);

                if (!DataMngr.ApplyDataPatch(ref alreadyLoadedData, loadedData, keyColumns, notImportantColumns))
                {
                    alreadyLoadedData = null;
                }

                if (alreadyLoadedData != null)
                {
                    // Apply filters again
                    if (enableFiltersToolStripMenuItem.Checked)
                    {
                        DataMngr.Instance.ApplyFiltersToDataTable(ref alreadyLoadedData, ref filteredData, Settings.Default.FilterFilePath, Enums.DataColumn.File);
                    }
                }
            }

            bgWorkerMain.ReportProgress(100);

            // Received the new chunk of data
            e.Result = alreadyLoadedData;
        }

        /// <summary>
        /// The patch data was loaded, reset everything
        /// </summary>
        /// <param name="sender">The sender.</param>
        /// <param name="e">The <see cref="System.ComponentModel.RunWorkerCompletedEventArgs"/> instance containing the event data.</param>
        private void Event_LoadedPatchData(object sender, RunWorkerCompletedEventArgs e)
        {
            this.SetStatus("Processing data...", Resources.LoadPatch, false);

            if (!bgWorkerMain.CancellationPending)
            {
                DataTable loadedData = (DataTable)e.Result;

                if (loadedData == null)
                {
                    MessageBox.Show(this,
                                    string.Concat("Patch data could not be loaded. This could be caused by the following reasons:",
                                                  Environment.NewLine,
                                                  "- You do not have access to some of the files",
                                                  Environment.NewLine,
                                                  "- Invalid path was supplied",
                                                  Environment.NewLine,
                                                  "- Other reasons"),
                                    Settings.Default.AppName,
                                    MessageBoxButtons.OK,
                                    MessageBoxIcon.Exclamation);

                    bgWorkerMain.DoWork -= new DoWorkEventHandler(Event_LoadPatchData);
                    bgWorkerMain.RunWorkerCompleted -= new RunWorkerCompletedEventHandler(Event_LoadedPatchData);
                    bgWorkerMain.ProgressChanged -= new ProgressChangedEventHandler(ProgressChanged);
                    //grdResx.RowsAdded += new DataGridViewRowsAddedEventHandler(dataGridResx_RowsAdded);

                    //this.SetStatus("Patching failed", null, true);
                    this.EndJob(true, "Patching failed", false);
                }
                else
                {
                    grdResx.DataSource = null;
                    grdResx.DataSource = loadedData;
                    this.AutoSizeGridColumns();

                    this.NeedsSave = true;

                    bgWorkerMain.DoWork -= new DoWorkEventHandler(Event_LoadPatchData);
                    bgWorkerMain.RunWorkerCompleted -= new RunWorkerCompletedEventHandler(Event_LoadedPatchData);
                    bgWorkerMain.ProgressChanged -= new ProgressChangedEventHandler(ProgressChanged);
                    //grdResx.RowsAdded += new DataGridViewRowsAddedEventHandler(dataGridResx_RowsAdded);

                    //this.SetStatus("Patching successfully completed", null, true);
                    this.EndJob(true, "Patching successfully completed", true);
                }
            }
            else
            {
                bgWorkerMain.DoWork -= new DoWorkEventHandler(Event_LoadPatchData);
                bgWorkerMain.RunWorkerCompleted -= new RunWorkerCompletedEventHandler(Event_LoadedPatchData);
                bgWorkerMain.ProgressChanged -= new ProgressChangedEventHandler(ProgressChanged);
                //grdResx.RowsAdded += new DataGridViewRowsAddedEventHandler(dataGridResx_RowsAdded);

                //this.SetStatus("Patching cancelled", null, true);
                this.EndJob(true, "Patching cancelled", false);
            }

            //this.Busy = false;
            //this.Enablings();
        }

        /// <summary>
        /// Loads a patch package that updates the values of the original data
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void tsbLoadPatch_Click(object sender, EventArgs e)
        {
            loadPatchToolStripMenuItem_Click(sender, e);
        }
        #endregion Load patch

        #region Grid context menu
        /// <summary>
        /// Handles the Opening event of the contextMenuStripGrid control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.ComponentModel.CancelEventArgs"/> instance containing the event data.</param>
        private void contextMenuStripGrid_Opening(object sender, CancelEventArgs e)
        {
            #region Select whole rows
            List<int> rowIndexes = new List<int>();

            foreach (DataGridViewCell cell in grdResx.SelectedCells)
            {
                if (!rowIndexes.Contains(cell.RowIndex))
                {
                    rowIndexes.Add(cell.RowIndex);
                }
            }

            foreach (int i in rowIndexes)
            {
                // Do not select the last/addnew row
                if (!grdResx.Rows[i].IsNewRow)
                {
                    grdResx.Rows[i].Selected = true;
                }
            }
            #endregion Select whole rows

            #region Enablings
            if (grdResx.SelectedRows.Count == 0)
            {
                addToIgnoreListToolStripMenuItem.Enabled = false;
            }
            else
            {
                addToIgnoreListToolStripMenuItem.Enabled = true;
            }
            #endregion Enablings
        }

        /// <summary>
        /// Handles the Click event of the addToIgnoreListToolStripMenuItem control.
        /// </summary>
        /// <param name="sender">The source of the event.</param>
        /// <param name="e">The <see cref="System.EventArgs"/> instance containing the event data.</param>
        private void addToIgnoreListToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (grdResx.SelectedRows.Count > 0)
            {
                try
                {
                    // Read the existing filters
                    List<string> filters = DataMngr.Instance.GetFilters(Settings.Default.FilterFilePath, true);

                    // Add new filters
                    string filter = string.Empty;
                    int counter = 0;
                    foreach (DataGridViewRow dr in grdResx.SelectedRows)
                    {
                        filter = dr.Cells[Enums.DataColumn.File].Value.ToString();
                        if (!filters.Contains(filter))
                        {
                            filters.Add(filter);
                            counter++;
                        }
                    }

                    DataMngr.Instance.SaveFilters(Settings.Default.FilterFilePath, filters);

                    MessageBox.Show(this, string.Format("{0} files added to filters.", counter), Settings.Default.AppName, MessageBoxButtons.OK, MessageBoxIcon.Information);

                    // Reload data/Apply filter
                    this.ApplyFilters();
                }
                catch (Exception ex)
                {
                    log.Error("Error while adding file to filter", ex);
                    MessageBox.Show(this, string.Format("Error while adding file '{0}' to filter. Please try again.", grdResx.SelectedRows[0].Cells[Enums.DataColumn.File].Value), Settings.Default.AppName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }
            }
            else
            {
                MessageBox.Show(this, "Please select a single whole row in the grid first then try again.", Settings.Default.AppName, MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
        }
        #endregion Grid context menu
        #endregion Events
    }
}
